// Package trace trace/trace.go
package trace

import (
	"bytes"
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

// goroutineSpace 用于在堆栈信息中识别goroutine ID
var goroutineSpace = []byte("goroutine ")

// 定义ANSI转义序列颜色常量,用于控制台输出着色
const (
	colorReset   = "\033[0m"
	colorGreen   = "\033[32m"
	colorBlue    = "\033[34m"
	colorMagenta = "\033[35m"
)

// curGoroutineID 获取当前goroutine的ID
func curGoroutineID() uint64 {
	// 分配一个足够大的缓冲区来存储堆栈信息
	b := make([]byte, 64)
	// 获取当前goroutine的堆栈信息
	b = b[:runtime.Stack(b, false)]
	// 移除前缀"goroutine "
	b = bytes.TrimPrefix(b, goroutineSpace)
	// 查找第一个空格的位置,它标志着goroutine ID的结束
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		panic(fmt.Sprintf("未找到空格: %q", b))
	}
	// 将goroutine ID从字节切片转换为uint64
	n, err := strconv.ParseUint(string(b[:i]), 10, 64)
	if err != nil {
		panic(fmt.Sprintf("解析goroutine ID失败: %q: %v", b, err))
	}
	return n
}

// printTrace 打印格式化的跟踪信息
func printTrace(id uint64, name string, line int, arrow string, indent int) {
	indents := strings.Repeat("    ", indent)
	fmt.Printf("%sg[%05d]%s:%s%s%s%s%s:%s%d%s\n",
		colorGreen, id, colorReset,
		indents, arrow,
		colorBlue, name, colorReset,
		colorMagenta, line, colorReset)
}

// goroutineIndent 使用sync.Map存储每个goroutine的缩进级别
var goroutineIndent sync.Map

// Trace 函数用于跟踪函数调用,返回一个用于结束跟踪的函数
func Trace() func() {
	// 获取调用者的程序计数器、文件名和行号
	pc, _, line, ok := runtime.Caller(1)
	if !ok {
		panic("未找到调用者")
	}

	// 获取函数信息
	fn := runtime.FuncForPC(pc)
	name := fn.Name()
	gid := curGoroutineID()

	// 原子操作更新缩进级别
	indentVal, _ := goroutineIndent.LoadOrStore(gid, int64(0))
	indent := indentVal.(int64)
	goroutineIndent.Store(gid, indent+1)

	// 打印函数进入的跟踪信息
	printTrace(gid, name, line, "->", int(indent)+1)

	// 返回一个闭包函数,用于在函数退出时打印跟踪信息
	return func() {
		/*		// 原子操作更新缩进级别
				indentVal, _ := goroutineIndent.Load(gid)
				indent := indentVal.(int64)
				goroutineIndent.Store(gid, indent-1)
				// 打印函数退出的跟踪信息
				printTrace(gid, name, line, "<-", int(indent))
		*/
	}
}
